package org.bigdata.framework.core.log.aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.bigdata.framework.core.log.annotation.SysLog;
import org.bigdata.framework.core.log.enums.BusinessStatus;
import org.bigdata.framework.core.model.Log;
import org.bigdata.framework.core.model.Member;
import org.bigdata.framework.core.model.User;
import org.bigdata.framework.core.protocol.IBaseService;
import org.bigdata.framework.core.protocol.IPublicService;
import org.bigdata.framework.core.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;



/**
 * 系统日志切点类
 * @author linrx
 *
 */
@Aspect
@Component
public class LogAopAction {
	private  static  final Logger logger = LoggerFactory.getLogger(LogAopAction. class);
	
	private static final ThreadLocal<Date> beginTimeThreadLocal =
			new NamedThreadLocal<Date>("ThreadLocal beginTime");
	private static final ThreadLocal<Log> logThreadLocal = 
			new NamedThreadLocal<Log>("ThreadLocal log");
	
	private static final ThreadLocal<Object> currentUser=new NamedThreadLocal<>("ThreadLocal user");
	
	@Autowired(required=false)
	private HttpServletRequest request;
	
	@Autowired
	private IBaseService baseService;
	@Autowired
	private IPublicService publicService;
	/**
	 * Service层切点 
	 */
/*	@Pointcut("@annotation(com.myron.ims.annotation.SystemServiceLog)")
	public void serviceAspect(){}*/
	
	/**
	 * Controller层切点 注解拦截
	 * org.bigdata.framework.core.controller
	 */
	/*@Pointcut("execution(* org.bigdata.framework.core.controller..*.*(..))")  */
	@Pointcut("execution(* org.bigdata.framework.core.controller..*.*(..))")
	public void controllerAspect(){}
	
	/**
	 * 方法规则拦截
	 * org.bigdata.framework.core.aop
	 *//*
	@Pointcut("execution(* org.bigdata.framework.core.controller.*.*(..))")
	public void controllerPointerCut(){}*/
	/**
	 * 前置通知 用于拦截Controller层记录用户的操作的开始时间
	 * @param joinPoint 切点
	 * @throws InterruptedException 
	 */
	@Before("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) throws InterruptedException{
		Date beginTime=new Date();
		beginTimeThreadLocal.set(beginTime);
		//debug模式下 显式打印开始时间用于调试
		if (logger.isDebugEnabled()){
	        logger.debug("开始计时: {}  URI: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
	        	.format(beginTime), request.getRequestURI());
		}
		
		//读取session中的用户 
		HttpSession session = request.getSession();       
	    Object userid= session.getAttribute("userid");    
		currentUser.set(userid);

	}
	
	/**
	 * 后置通知 用于拦截Controller层记录用户的操作
	 * @param joinPoint 切点
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	@SuppressWarnings("unchecked")
	@After("controllerAspect()")
	public void doAfter(JoinPoint joinPoint) throws NoSuchMethodException, SecurityException {
		Object userid = currentUser.get();
		if(null==userid){
			return;
		}
        // 拦截的实体类，就是当前正在执行的controller
        Object target = joinPoint.getTarget();
        // 拦截的方法名称。当前正在执行的方法
        String methodName = joinPoint.getSignature().getName();
        // 拦截的方法参数
        Object[] args = joinPoint.getArgs();
        // 拦截的放参数类型
        Signature sig = joinPoint.getSignature();
        String remoteAddr=request.getRemoteAddr();//请求的IP
    	String requestUri=request.getRequestURI();//请求的Uri
    	Map<String,String[]> params=request.getParameterMap(); //请求提交的参数
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Class[] parameterTypes = msig.getMethod().getParameterTypes();
        Method method = null;
        try {
            method = target.getClass().getMethod(methodName, parameterTypes);
            if (null != method) {
                // 判断是否包含自定义的注解，说明一下这里的SystemLog就是我自己自定义的注解
                if (method.isAnnotationPresent((Class<? extends Annotation>) SysLog.class)) {
                	SysLog systemlog = method.getAnnotation(SysLog.class);
                	String username="";
                	String telephone="";
                	if(systemlog.operatorType().ordinal()==1){
                		User findUser = publicService.findUser(Integer.parseInt(userid.toString()));
                		username=findUser.getUsername();
                		telephone=findUser.getTelephone();
                		findUser=null;
                	}else{
                		Member findMember = publicService.findMember(Integer.parseInt(userid.toString()));
                		username=findMember.getUsername();
                		telephone=findMember.getTelephone();
                		findMember=null;
                	}
					
                	
                	Log log=new Log();
                	log.setText(systemlog.title());
                	log.setBusinessType(systemlog.businessType().ordinal());
                	log.setOperatorType(systemlog.operatorType().ordinal());
                	log.setCreateIpAddress(remoteAddr);
                	log.setUri(requestUri);
                	log.setMapToParams(params);
                	log.setCreateTime(DateUtils.getDateTime());
                	log.setCreateUser(username);
                	log.setCreateUser(username);
                	log.setTelephone(telephone);
                	log.setCreateUserId(userid.toString());
                 	baseService.saveLog(log);
                    logThreadLocal.set(log);
                } 
            } 
        } catch (NoSuchMethodException e1) {
            e1.printStackTrace();
        } catch (SecurityException e1) {
            e1.printStackTrace();
        }

      
	}
	
	/**
	 *  异常通知 
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")  
	public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		Log log = logThreadLocal.get();
		if(log != null){
			log.setBusinessType(BusinessStatus.FAIL.ordinal());
			log.setException(e.toString());
			baseService.editLog(log);			
		}
	}

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * 
	 * @param joinPoint 切点
	 * @return 方法描述
	 */
	public static String getControllerMethodDescription2(JoinPoint joinPoint) {
		/*MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();*/
	
		return null;
	}

	/**
	 * 保存日志线程
	 * 
	 * @author lin.r.x
	 *
	 */
	private static class SaveLogThread implements Runnable {
		private Log log;
		private IBaseService baseService;

		public SaveLogThread(Log log, IBaseService baseService) {
			this.log = log;
			this.baseService = baseService;
		}

		@Override
		public void run() {
			baseService.saveLog(log);
		}
	}

	/**
	 * 日志更新线程
	 * 
	 * @author lin.r.x
	 *
	 */
	private static class UpdateLogThread extends Thread {
		private Log log;
		private IBaseService baseService;

		public UpdateLogThread(Log log, IBaseService baseService) {
			super(UpdateLogThread.class.getSimpleName());
			this.log = log;
			this.baseService = baseService;
		}

		@Override
		public void run() {
			this.baseService.editLog(log);
		}
	}
}
